package com.example.sm2.util;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.security.SecureRandom;
import java.security.Security;
import java.util.*;

/**
 * SM2国密算法工具类
 * 提供密钥对生成、加密、解密功能
 */
@Component
public class SM2Utils {
    
    // 静态初始化，添加BouncyCastleProvider支持
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    
    // 获取SM2曲线参数
    private static final X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    private static final ECDomainParameters ecDomainParameters = new ECDomainParameters(
            x9ECParameters.getCurve(),
            x9ECParameters.getG(),
            x9ECParameters.getN()
    );
    
    // 定义需要加密的字段名
    private static final Set<String> SENSITIVE_FIELDS = new HashSet<>(Arrays.asList(
            "idCard", "phone", "bankCard", "password", "email"
    ));
    
    /**
     * 生成SM2密钥对
     * @return 包含公钥和私钥的Map
     */
    public Map<String, String> generateKeyPair() {
        // 初始化密钥生成器
        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
        ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(
                ecDomainParameters, 
                new SecureRandom()
        );
        keyPairGenerator.init(keyGenerationParameters);
        
        // 生成密钥对
        AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();
        ECPublicKeyParameters publicKeyParameters = (ECPublicKeyParameters) keyPair.getPublic();
        ECPrivateKeyParameters privateKeyParameters = (ECPrivateKeyParameters) keyPair.getPrivate();
        
        // 获取公钥点
        ECPoint publicKeyPoint = publicKeyParameters.getQ();
        
        // 转换为十六进制字符串
        String publicKey = Hex.toHexString(publicKeyPoint.getEncoded(false));
        String privateKey = Hex.toHexString(privateKeyParameters.getD().toByteArray());
        
        // 存储并返回
        Map<String, String> keyMap = new HashMap(2);
        keyMap.put("publicKey", publicKey);
        keyMap.put("privateKey", privateKey);
        
        return keyMap;
    }
    
    /**
     * SM2加密
     * @param publicKey 公钥(十六进制字符串)
     * @param data 待加密数据
     * @return 加密后的数据(十六进制字符串)
     */
    public String encrypt(String publicKey, String data) {
        try {
            // 解码公钥
            byte[] publicKeyBytes = Hex.decode(publicKey);
            ECPoint publicKeyPoint = x9ECParameters.getCurve().decodePoint(publicKeyBytes);
            ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(
                    publicKeyPoint, 
                    ecDomainParameters
            );
            
            // 初始化加密引擎
            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
            
            // 执行加密
            byte[] dataBytes = data.getBytes();
            byte[] encryptedData = sm2Engine.processBlock(dataBytes, 0, dataBytes.length);
            
            return Hex.toHexString(encryptedData);
        } catch (InvalidCipherTextException e) {
            throw new RuntimeException("SM2 encryption failed", e);
        }
    }
    
    /**
     * SM2解密
     * @param privateKey 私钥(十六进制字符串)
     * @param encryptedData 加密后的数据(十六进制字符串)
     * @return 解密后的原始数据
     */
    public String decrypt(String privateKey, String encryptedData) {
        try {
            // 解码私钥
            byte[] privateKeyBytes = Hex.decode(privateKey);
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(
                    new java.math.BigInteger(1, privateKeyBytes),
                    ecDomainParameters
            );
            
            // 初始化解密引擎
            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(false, privateKeyParameters);
            
            // 执行解密
            byte[] encryptedDataBytes = Hex.decode(encryptedData);
            byte[] decryptedData = sm2Engine.processBlock(encryptedDataBytes, 0, encryptedDataBytes.length);
            
            return new String(decryptedData);
        } catch (InvalidCipherTextException e) {
            throw new RuntimeException("SM2 decryption failed", e);
        }
    }
    
    /**
     * 对对象中的关键字段进行加密
     * @param publicKey SM2公钥
     * @param object 待加密的对象，可以是实体类或List
     * @return 加密后的对象
     */
    public Object encryptSensitiveFields(String publicKey, Object object) {
        if (object == null) {
            return null;
        }
        
        // 如果是List类型，递归处理每个元素
        if (object instanceof List) {
            List<?> list = (List<?>) object;
            List<Object> encryptedList = new ArrayList<>(list.size());
            for (Object item : list) {
                encryptedList.add(encryptSensitiveFields(publicKey, item));
            }
            return encryptedList;
        }
        
        // 处理单个对象
        return encryptObjectFields(publicKey, object);
    }
    
    /**
     * 对对象中的关键字段进行解密
     * @param privateKey SM2私钥
     * @param object 待解密的对象，可以是实体类或List
     * @return 解密后的对象
     */
    public Object decryptSensitiveFields(String privateKey, Object object) {
        if (object == null) {
            return null;
        }
        
        // 如果是List类型，递归处理每个元素
        if (object instanceof List) {
            List<?> list = (List<?>) object;
            List<Object> decryptedList = new ArrayList<>(list.size());
            for (Object item : list) {
                decryptedList.add(decryptSensitiveFields(privateKey, item));
            }
            return decryptedList;
        }
        
        // 处理单个对象
        return decryptObjectFields(privateKey, object);
    }
    
    /**
     * 加密单个对象的关键字段
     * @param publicKey SM2公钥
     * @param object 待加密对象
     * @return 加密后的对象
     */
    private Object encryptObjectFields(String publicKey, Object object) {
        if (object == null) {
            return null;
        }
        
        // 创建对象副本以避免修改原始对象
        Object encryptedObject;
        try {
            encryptedObject = object.getClass().getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Failed to create instance of object", e);
        }
        
        // 获取对象的所有字段
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(object);
                // 如果字段名在敏感字段列表中且值不为null，则进行加密
                if (SENSITIVE_FIELDS.contains(field.getName()) && value != null) {
                    String encryptedValue = encrypt(publicKey, value.toString());
                    field.set(encryptedObject, encryptedValue);
                } else {
                    // 非敏感字段直接复制
                    field.set(encryptedObject, value);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to access field: " + field.getName(), e);
            }
        }
        
        return encryptedObject;
    }
    
    /**
     * 解密单个对象的关键字段
     * @param privateKey SM2私钥
     * @param object 待解密对象
     * @return 解密后的对象
     */
    private Object decryptObjectFields(String privateKey, Object object) {
        if (object == null) {
            return null;
        }
        
        // 创建对象副本以避免修改原始对象
        Object decryptedObject;
        try {
            decryptedObject = object.getClass().getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Failed to create instance of object", e);
        }
        
        // 获取对象的所有字段
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(object);
                // 如果字段名在敏感字段列表中且值不为null，则进行解密
                if (SENSITIVE_FIELDS.contains(field.getName()) && value != null) {
                    String decryptedValue = decrypt(privateKey, value.toString());
                    field.set(decryptedObject, decryptedValue);
                } else {
                    // 非敏感字段直接复制
                    field.set(decryptedObject, value);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to access field: " + field.getName(), e);
            }
        }
        
        return decryptedObject;
    }
}
